import { useRef, useMemo, useState, useCallback } from 'react';
import throttle from 'lodash.throttle';
import VisibilityChange from '@ice/appear';
import { Form, Input, Empty, Modal, Button, message, type InputRef } from 'antd';

import Menu from '@/components/Menu';
import Icon from '@/components/Icon';
import { clamp } from '@/common/utils';
import type { Props, SelectedData, HandleCollect } from './interface';

import styles from './index.module.scss';

/** 收藏项高度 */
const ITEM_HEIGHT = 30;
/** 菜单ID */
const MENU_ID = 'qrcode_item';

/**
 * 收藏列表
 */
export default function Collect(props: Props) {
  const { className, collectData, textareaRef, handleAction, updateCollect } = props;

  // 输入框打开
  const [showModal, setShowModal] = useState(false);
  // 显示列表顶部遮罩
  const [maskTopShow, setMaskTopShow] = useState(false);
  // 显示列表底部遮罩
  const [maskBottomShow, setMaskBottomShow] = useState(true);
  // 选中的收藏项下标
  const [activeIndex, setActiveIndex] = useState<number | undefined>(undefined);

  /** 右击菜单 */
  const menuRef = useRef<any>(null);
  /** 上次创建的时间戳 */
  const prevTimestamp = useRef(0);
  const inputRef = useRef<InputRef>(null);
  const listRef = useRef<HTMLDivElement>(null);
  /** 选中项 */
  const selectedRef = useRef<SelectedData | null>(null);

  /** 遮罩是否渲染 */
  const shouldRenderMask = useMemo(() => {
    const height = listRef.current?.clientHeight;
    if (!height) return false;
    return ITEM_HEIGHT * collectData.length > height;
  }, [collectData]);

  const [form] = Form.useForm();
  const { resetFields, validateFields } = form;
  const [messageApi, contextHolder] = message.useMessage();

  /**
   * 取消收藏
   */
  const onCancel = useCallback(() => {
    resetFields();
    setShowModal(false);
  }, [resetFields]);

  /**
   * 新增收藏
   */
  const addCollect = useCallback(async () => {
    const res = await validateFields().catch(() => {});
    if (res) {
      const timestamp = Date.now();
      prevTimestamp.current = timestamp;
      onCancel();
      updateCollect(collectData.concat({ name: res.collectName, url: textareaRef.current!.value, timestamp }));
      // 自动滚动到新增那一条
      requestAnimationFrame(() => listRef.current!.scrollTo({ top: listRef.current!.scrollHeight }));
    }
  }, [collectData, onCancel, updateCollect, validateFields]);

  /**
   * 处理菜单选择的共同逻辑
   */
  const handleCollect: HandleCollect = useCallback((func) => {
    return () => {
      const data = selectedRef.current;
      if (!data) return;
      func(data);
    };
  }, []);

  /**
   * 列表项居中
   */
  const scrollToCenter = useCallback((index: number) => {
    const height = listRef.current!.clientHeight;
    const targetScrollTop = clamp(index * ITEM_HEIGHT - height / 2 + ITEM_HEIGHT / 2, 0, height);
    listRef.current!.scrollTo({ top: targetScrollTop });
  }, []);

  /**
   * 确认编辑
   */
  const editCollect = useCallback(
    handleCollect(({ url, index }) => {
      messageApi.info('修改后会自动保存');
      handleAction(url, index);
      setActiveIndex(index);
      scrollToCenter(index);
    }),
    [messageApi, handleAction, handleCollect, scrollToCenter],
  );

  /**
   * 确认删除
   */
  const delCollect = useCallback(
    handleCollect(({ index, active }) => {
      const delDom = listRef.current!.childNodes[index];
      const cb = () => {
        collectData.splice(index, 1);
        updateCollect(collectData.slice());
        if (active) {
          handleAction(undefined);
          setActiveIndex(undefined);
        }
        delDom?.removeEventListener('animationend', cb);
      };
      delDom?.addEventListener('animationend', cb);
      // 增加删除动画效果
      if (delDom instanceof HTMLElement) {
        delDom.style.animationDelay = 'initial';
        delDom.classList.add(styles.exit);
      } else {
        cb();
      }
    }),
    [collectData, handleAction, updateCollect, handleCollect],
  );

  /**
   * 收藏项右击
   */
  const handleContextMenu = useCallback(
    throttle(
      (event, data) => {
        selectedRef.current = data;
        menuRef.current!.show({ event });
      },
      300,
      { trailing: false },
    ),
    [],
  );

  /**
   * 收藏项点击
   */
  const handleClick = useCallback(
    throttle(
      ({ url, index, active }) => {
        if (active) {
          handleAction(undefined);
          setActiveIndex(undefined);
        } else {
          handleAction(url);
          setActiveIndex(index);
          scrollToCenter(index);
        }
      },
      300,
      { trailing: false },
    ),
    [handleAction, scrollToCenter],
  );

  /**
   * 渲染收藏列表项
   */
  const renderCollectList = useCallback(() => {
    if (collectData.length === 0) return <Empty description="收藏为空" image={Empty.PRESENTED_IMAGE_SIMPLE} />;

    return collectData.map(({ url, name, timestamp }, index) => {
      const isFirst = index === 0;
      const isLast = index === collectData.length - 1;
      const active = index === activeIndex;
      const data = { url, index, active };
      let text = <span>{name}</span>;

      if (shouldRenderMask && (isFirst || isLast)) {
        const setFunc = isFirst ? setMaskTopShow : setMaskBottomShow;
        text = (
          <VisibilityChange onAppear={() => setFunc(false)} onDisappear={() => setFunc(true)}>
            {text}
          </VisibilityChange>
        );
      }

      return (
        <div
          key={timestamp}
          x-class={[styles.item, { [styles.active]: active }]}
          onClick={() => handleClick(data)}
          onContextMenu={(e) => handleContextMenu(e, data)}
          style={prevTimestamp.current === timestamp ? {} : { animationDelay: `${index * 50}ms` }}
        >
          {text}
        </div>
      );
    });
  }, [collectData, activeIndex, shouldRenderMask, handleClick, handleContextMenu]);

  return (
    <>
      <div x-class={[styles.collect, className]}>
        <div className={styles.title} onClick={() => setShowModal(true)}>
          <Icon className={styles.icon} />
          收藏
        </div>

        <div className={styles.listWrap}>
          <div x-if={shouldRenderMask} x-class={[styles.maskTop, { [styles.show]: maskTopShow }]} />
          <div className={styles.list} ref={listRef}>
            {renderCollectList()}
          </div>
          <div x-if={shouldRenderMask} x-class={[styles.maskBottom, { [styles.show]: maskBottomShow }]} />
        </div>
      </div>

      <Modal
        width={265}
        title="添加收藏"
        open={showModal}
        maskClosable={false}
        footer={
          <Button type="primary" onClick={addCollect}>
            收藏
          </Button>
        }
        onCancel={onCancel}
        afterOpenChange={(visible) => visible && inputRef.current!.focus()}
      >
        <Form form={form}>
          <Form.Item
            name="collectName"
            rules={[
              { required: true, message: '请输入收藏名称' },
              { type: 'string', max: 12, message: '收藏名称最多输入12个字' },
            ]}
          >
            <Input
              ref={inputRef}
              allowClear
              autoComplete="off"
              placeholder="请输入收藏名称"
              onPressEnter={addCollect}
            />
          </Form.Item>
        </Form>
      </Modal>

      <Menu
        ref={menuRef}
        id={MENU_ID}
        items={[
          { icon: 'edit', name: '编辑', onClick: editCollect },
          { icon: 'delete', name: '删除', onClick: delCollect },
        ]}
      />

      {contextHolder}
    </>
  );
}
